package com.zb.frame.base.config.shiro;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zb.frame.base.enums.AuthenticationStatus;
import com.zb.frame.base.model.R;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ShiroFilter extends AuthenticatingFilter {

    private static final Logger logger = LoggerFactory.getLogger(ShiroFilter.class);

    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        String token = getRequestToken((HttpServletRequest) servletRequest);
        if (StringUtils.isEmpty(token))
            return null ;
        // System.out.println("------createToken------");
        return new SelfToken(token);
    }

    /**
     * 请求后验证是否通过
     * 使用无状态会话，全不通过；只过滤OPTIONS请求 调用onAccessDenied方法
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // System.out.println("------isAccessAllowed------false------");
        return request.getMethod().equals(RequestMethod.OPTIONS.name());
    }

    /**
     * 认证未通过调用的方法
     * 使用无状态会话，在此处取请求头header中的Authorization的token / 参数中的 token
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        //获取token，如果token不存在，直接返回401
        String token = getRequestToken((HttpServletRequest) servletRequest);
        // System.out.println("------onAccessDenied------token------" + token);
        if (StringUtils.isEmpty(token)) {
            logger.warn("没有通过认证，token不存在！");
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            response.setCharacterEncoding("UTF-8");
            String json = JSON.toJSONString(R.error(401, "invalid token","没有通过认证，token不存在！"));
            response.getWriter().print(json);
            return false;
        }
        // 转到ShiroRealm的登录认证
        return executeLogin(servletRequest, servletResponse);
    }

    /**
     * 登录失败后执行的方法
     * 捕抓错误处理
     * @return
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        try {
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            AuthenticationStatus type = AuthenticationStatus.valueOf(throwable.getMessage());
            // System.out.println(type.name());
            String json = "";
            if (AuthenticationStatus.EXPIRED == type) {
                // accessToken 过期
                json = JSON.toJSONString(R.error(2001, type.getDesc(),"认证登录失败！"));
            } else {
                json = JSON.toJSONString(R.error(401, type.getDesc(),"认证登录失败！"));
            }
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {
            // e1.printStackTrace();
        }
        return false;
    }

    /**
     * 从请求头header中/参数中获取token
     */
    private String getRequestToken(HttpServletRequest httpRequest){
        String token = httpRequest.getHeader("Authorization");
        if(StringUtils.isEmpty(token))
            token = httpRequest.getParameter("token");
        return token;
    }
}
